package com.example.system.service.impl;

import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.session.TokenSign;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.common.constant.RedisConstant;
import com.example.common.constant.SystemConstant;
import com.example.common.redis.RedisUtils;
import com.example.common.response.R;
import com.example.common.utils.GzipUtils;
import com.example.common.utils.StringUtils;
import com.example.common.webSocket.SendSocket;
import com.example.common.webSocket.WebSocket;
import com.example.system.domain.bo.PageBo;
import com.example.system.domain.bo.UserBo;
import com.example.system.domain.entity.Process;
import com.example.system.domain.entity.*;
import com.example.system.domain.model.LoginTempInfo;
import com.example.system.domain.vo.LoginVo;
import com.example.system.domain.vo.PageVo;
import com.example.system.domain.vo.UserVo;
import com.example.system.mapper.*;
import com.example.system.others.utils.CheckUtils;
import com.example.system.service.DictService;
import com.example.system.service.ItemService;
import com.example.system.service.UserService;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RoleMapper roleMapper;
    @Autowired
    private LoginMapper loginMapper;
    @Autowired
    private UserRoleMapper userRoleMapper;
    @Autowired
    private ItemMapper itemMapper;
    @Autowired
    private ProjectMapper projectMapper;
    @Autowired
    private ActivationMapper activationMapper;
    @Autowired
    private ScoreMapper scoreMapper;
    @Autowired
    private FriendMapper friendMapper;
    @Autowired
    private ProcessMapper processMapper;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private DictService dictService;
    @Autowired
    private GzipUtils gzipUtils;
    @Autowired
    private ItemService itemService;
    @Autowired
    private ImageMapper imageMapper;
    @Autowired
    private LogMapper logMapper;
    @Autowired
    private UserPermMapper userPermMapper;

    /**
     * 搜索 用户/账户 列表-分页
     */
    @Override
    public PageVo getPageVo(PageBo pageBo) {
        List<UserVo> list = this.getList();
        return new PageVo(pageBo, list);
    }

    /**
     * 搜索 用户/账户 列表-不分页
     */
    @Override
    public List<UserVo> getList() {
        return userMapper.getUserListVo();
    }

    /**
     * 模糊查询用户列表
     * 查询账号列、姓名列、用户名列、账号权限列(角色名称)、邮箱列、手机号列
     */
    @Override
    public R<PageVo> getPageVoSearch(UserBo userBo, PageBo pageBo) {
        String search = userBo.getUsername();
        List<UserVo> userList = this.getList();
        if (StringUtils.isNotEmpty(search)) {
            userList = userList.stream().filter(o ->
                    (StringUtils.isNotEmpty(o.getAccount()) && o.getAccount().contains(search)) || (StringUtils.isNotEmpty(o.getName()) && o.getName().contains(search)) || (StringUtils.isNotEmpty(o.getUsername()) && o.getUsername().contains(search)) || (StringUtils.isNotEmpty(o.getName()) && o.getRoleName().contains(search)) || (StringUtils.isNotEmpty(o.getEmail()) && o.getEmail().contains(search)) || (StringUtils.isNotEmpty(o.getMobile()) && o.getMobile().contains(search))
            ).collect(Collectors.toList());
        }
        if (StringUtils.isNotEmpty(userList)) {
            return R.ok(new PageVo(pageBo, userList));
        }
        return R.info("没有符合条件的数据", new PageVo());
    }

    /**
     * 获取 用户/账户 头像，并转为二进制数组
     */
    @Override
    public byte[] getUserImage(String account) {
        int imageId = userMapper.getUserByAccount(account).getImageId();
        String image = imageMapper.selectById(imageId).getImage();
        return gzipUtils.compress(image);
    }

    /**
     * 回显个人信息
     * 1.用户名 username
     * 2.角色名称 roleName
     * 3.角色ID roleId
     * 4.上次登录事件 time
     * 5.上次登录IP ip
     * 6.头像 image
     * 7.手机号码 mobile
     * 8.邮箱 email
     * 9.账号创建时间 createTime
     */
    @Override
    public Map<String, Object> getInfo() {
        String account = (String) StpUtil.getLoginId();
        User user = userMapper.getUserByAccount(account);
        Map<String, Object> map = new HashMap<>();
        map.put("username", user.getUsername());
        map.put("mobile", user.getMobile());
        map.put("email", user.getEmail());
        map.put("sex", user.getSex());
        map.put("createTime", user.getCreateTime().toString().replace("T", " "));
        Role role = roleMapper.getRoleByAccount(account);
        map.put("roleName", role.getRoleName());
        map.put("roleId", role.getRoleId());
        LoginVo loginVo = loginMapper.getLastByAccount(account);
        map.put("time", loginVo.getTime().toString().replace("T", " "));
        map.put("ip", loginVo.getIp());
        byte[] image = getUserImage(account);
        map.put("image", image);
        return map;
    }

    /**
     * 修改账号状态
     */
    @Override
    @SneakyThrows
    public String ChangeStatus(UserBo bo) {
        User user = userMapper.selectById(bo.getUserId());
        int status = user.getStatus();
        status = (status + 1) % 2;
        user.setStatus(status);
        userMapper.updateById(user);
        if (status == SystemConstant.USER_STATUS_NORMAL) {
            return "激活成功";
        }
        if (status == SystemConstant.USER_STATUS_DISABLE) {
            return "禁用成功";
        }
        throw new Exception("修改账号状态出现错误");
    }

    /**
     * 批量删除
     */
    @Override
    public String deleteList(List<Integer> ids) {
        ids.forEach(this::deleteUser);
        return "删除成功";
    }

    /**
     * 删除 用户/账户
     */
    @Override
    public String deleteUser(Integer userId) {
        User user = userMapper.getUserByUserId(userId);
        String account = user.getAccount();
        //删除用户表的关联表
        //1.用户权限表 sys_user_perm
        LambdaQueryWrapper<UserPerm> lqwUserPerm = new LambdaQueryWrapper<>();
        lqwUserPerm.eq(UserPerm::getUserId, userId);
        userPermMapper.delete(lqwUserPerm);
        //2.项目内容表 sys_Project和项目表 sys_Item
        LambdaQueryWrapper<Item> lqwItem = new LambdaQueryWrapper<>();
        lqwItem.eq(Item::getCreateBy, userId);
        List<Item> items = itemMapper.selectList(lqwItem);
        if (!items.isEmpty()) {
            itemMapper.delete(lqwItem);
            LambdaQueryWrapper<Project> lqwProject = new LambdaQueryWrapper<>();
            for (Item i : items) lqwProject.eq(Project::getId, i.getProjectId());
            projectMapper.delete(lqwProject);
            itemService.updateItemsRedis();
        }
        //3.用户活跃表 sys_activation
        LambdaQueryWrapper<Activation> lqwActivation = new LambdaQueryWrapper<>();
        lqwActivation.eq(Activation::getUserId, userId);
        activationMapper.delete(lqwActivation);
        //4.评分表 sys_score
        LambdaQueryWrapper<Score> lqwScore = new LambdaQueryWrapper<>();
        lqwScore.eq(Score::getUserId, userId);
        scoreMapper.delete(lqwScore);
        //5.好友关系表 sys_friend
        LambdaQueryWrapper<Friend> lqwFriend1 = new LambdaQueryWrapper<>();
        LambdaQueryWrapper<Friend> lqwFriend2 = new LambdaQueryWrapper<>();
        lqwFriend1.eq(Friend::getUserIdA, userId);
        lqwFriend2.eq(Friend::getUserIdB, userId);
        friendMapper.delete(lqwFriend1);
        friendMapper.delete(lqwFriend2);
        //6.用户审核表 sys_process
        LambdaQueryWrapper<Process> lqwProcess = new LambdaQueryWrapper<>();
        lqwProcess.eq(Process::getUserId, userId);
        processMapper.delete(lqwProcess);
        //7.日志表 sys_log
        LambdaQueryWrapper<Log> lqwLog = new LambdaQueryWrapper<>();
        lqwLog.eq(Log::getAccount, account);
        logMapper.delete(lqwLog);
        //8.登录记录表 sys_login
        LambdaQueryWrapper<Login> lqwLogin = new LambdaQueryWrapper<>();
        lqwLogin.eq(Login::getAccount, account);
        loginMapper.delete(lqwLogin);
        //9.用户角色表 sys_user_role
        LambdaQueryWrapper<UserRole> lqwUserRole = new LambdaQueryWrapper<>();
        lqwUserRole.eq(UserRole::getUserId, userId);
        userRoleMapper.delete(lqwUserRole);
        //10.删除用户表信息 sys_user
        LambdaQueryWrapper<User> lqwUser = new LambdaQueryWrapper<>();
        lqwUser.eq(User::getUserId, userId);
        userMapper.delete(lqwUser);
        return "删除成功";
    }

    /**
     * 新增用户
     */
    @Override
    public String insertUser(UserBo bo) {
        this.checkUserBeforeInsert(bo);
        //校验没问题
        User user = new User();
        user.setUsername(user.getUsername());
        user.setName(user.getName());
        user.setMobile(user.getMobile());
        user.setRemark(user.getRemark());
        user.setEmail(user.getEmail());
        user.setStatus(SystemConstant.USER_STATUS_NORMAL);
        //用户输入的密码
        String password = bo.getPassword();
        //生成随机加密盐值
        String salt = IdUtil.simpleUUID();
        password = SaSecureUtil.sha256(password + salt);
        user.setSalt(salt);
        user.setPassword(password);
        user.setCreateTime(LocalDateTime.now());
        //给用户生成一张照片
        user.setImageId(SystemConstant.image_Id);
        //生成账号
        String db_user = RedisConstant.db_user;
        int userId = dictService.getId(db_user);
        String account = String.valueOf(userId + SystemConstant.billion - 1);
        user.setAccount(account);
        user.setUserId(userId);
        user.setUsername(bo.getUsername());
        user.setName(bo.getName());
        user.setEmail(bo.getEmail());
        user.setMobile(bo.getMobile());
        //添加用户信息
        userMapper.insertUser(user);
        //生成账号和id,并更新
        int userId = user.getUserId();
        String account = String.valueOf(userId + SystemConstant.billion - 1);
        user.setAccount(account);
        user.setUserId(userId);
        userMapper.updateByUserId(user);

        UserRole userRole = new UserRole();
        userRole.setUserId(userId);
        userRole.setRoleId(bo.getRoleId());
        userRoleMapper.insertUserRole(userRole);

        return "添加账号成功,账号为:" + account;
    }

    /**
     * 修改密码
     */
    @Override
    public R<String> updatePwd(UserBo bo) {
        String account = (String) StpUtil.getLoginId();
        User user = userMapper.getUserByAccount(account);
        String salt = user.getSalt();
        String pwd = SaSecureUtil.sha256(bo.getOldPassword() + salt);
        if (!pwd.equals(user.getPassword())) {
            return R.fail("修改密码失败，旧密码不正确");
        }
        pwd = SaSecureUtil.sha256(bo.getPassword() + salt);
        userMapper.updatePwd(user.getAccount(), pwd);
        return R.ok("修改密码成功");
    }

    /**
     * 强制退出
     */
    @Override
    public PageVo getOnlineList(PageBo pageBo) {
        StpLogic stpLogic = SaManager.getStpLogic("login");
        List<String> tokens = stpLogic.searchTokenValue("", -1, -1, true);
        // 保存所有用户id及对应的不同平台下的临时有效时长
        List<LoginTempInfo> list = new ArrayList<>();
        tokens.forEach(o -> {
            //真实token
            String token = o.substring(o.lastIndexOf(":") + 1);
            //token过期时间
            long loginTimeout = stpLogic.getTokenActivityTimeoutByToken(token);
            if (loginTimeout == -2) {
                // 当前 token 所代表的会话已经临时过期了, 直接跳过
                return;
            }
            //从token获取登录的账号
            String account = (String) stpLogic.getLoginIdByToken(token);
            String username = userMapper.getUserByAccount(account).getUsername();
            // 根据登录id和token, 获取对应的登录类型
            String mobile = null;
            SaSession session = stpLogic.getSessionByLoginId(account, false);
            if (session == null) {
                mobile = "";
            } else {
                // 遍历解析
                List<TokenSign> tokenSignList = session.tokenSignListCopy();
                for (TokenSign tokenSign : tokenSignList) {
                    if (tokenSign.getValue().equals(token)) {
                        mobile = tokenSign.getDevice();
                        break;
                    }
                }
            }
            // 每个用户id可以多次登录, 也可以在不同平台登录
            LoginTempInfo tempInfo = new LoginTempInfo(account, username, token, mobile, loginTimeout);
            // 同一个用户如果多端登录需要同时记录不同平台的时限
            list.add(tempInfo);
        });
        return new PageVo(pageBo, list);
    }

    @Override
    public void forcedOut(String account) {
        SendSocket socket = new SendSocket();
        socket.setType("forcedOut");
        socket.setMessage("您已被强制退出，请重新登陆或者联系管理员。");

        WebSocket webSocket = new WebSocket();
        webSocket.sendOneMessage(account, socket);
        StpUtil.logout(account);
    }

    /**
     * 更改账号信息
     */
    @Override
    public String updateUser(UserBo userBo) {
        this.checkUserBeforeUpdate(userBo);
        userMapper.updateByUserId(userBo);
        UserRole userRole = new UserRole();
        userRole.setRoleId(userBo.getRoleId());
        userRole.setUserId(userBo.getUserId());
        userRoleMapper.updateRoleId(userRole);
        return "修改成功";
    }

    /**
     * 添加用户前，检查用户数据
     */
    @SneakyThrows
    private void checkUserBeforeInsert(UserBo user) {
        this.checkUser(user);

        //检查密码
        String pwd = user.getPassword();
        if (StringUtils.isEmpty(pwd)) {
            throw new Exception("数据检查出现异常：密码为空");
        }
        if (pwd.length() < 8) {
            throw new Exception("数据检查出现异常：密码必须大于8位");
        }
        if (!CheckUtils.checkContainDigit(pwd) || CheckUtils.checkContainCase(pwd) || !CheckUtils.checkContainSpecialChar(pwd)) {
            throw new Exception("密码必须包含数字、大小写字母、特殊符号");
        }
    }

    /**
     * 修改用户前，检查用户数据
     */
    @SneakyThrows
    private void checkUserBeforeUpdate(UserBo user) {
        this.checkUser(user);

        //检查账号角色
        if (StringUtils.isEmpty(user.getRoleId())) {
            throw new Exception("数据检查出现异常：角色不能为空");
        }
    }

    /**
     * 通用检查
     */
    @SneakyThrows
    private void checkUser(User user) {
        //检查用户名
        String username = user.getUsername();
        if (StringUtils.isEmpty(username)) {
            throw new Exception("数据检查出现异常：用户名为空");
        }
        if (!(username.length() > 1 && username.length() <= 20)) {
            throw new Exception("数据检查出现异常：用户名的长度应为1到20个字符");
        }
        //不做用户名唯一校验，允许用户名重复

        //检查姓名
        String name = user.getName();
        if (StringUtils.isEmpty(name)) {
            throw new Exception("数据检查出现异常：姓名为空");
        }
        if (name.length() > 1 && name.length() <= 20) {
            throw new Exception("数据检查出现异常：姓名的长度应为1到20个字符");
        }
        //不做姓名唯一校验，允许姓名重复

        //校验手机号
        checkMobile(user.getMobile());
        //校验邮箱
        checkEmail(user.getEmail());
    }

    /**
     * 手机号码正则 验证手机号码格式
     * true 格式正确
     * 添加用户前，检查用户数据
     */
    @SneakyThrows
    private void checkUserBeforeInsert(UserBo user) {
        this.checkUser(user);

        //检查密码
        String pwd = user.getPassword();
        if (StringUtils.isEmpty(pwd)) {
            throw new Exception("数据检查出现异常：密码为空");
        }
        if (pwd.length() < 8) {
            throw new Exception("数据检查出现异常：密码必须大于8位");
        }
        if (!CheckUtils.checkContainDigit(pwd) || !CheckUtils.checkContainCase(pwd) || !CheckUtils.checkContainSpecialChar(pwd)) {
            throw new Exception("密码必须包含数字、大小写字母、特殊符号");
        }
    }

    /**
     * 修改用户前，检查用户数据
     */
    @SneakyThrows
    private void checkUserBeforeUpdate(UserBo user) {
        this.checkUser(user);

        //检查账号角色
        if (StringUtils.isEmpty(user.getRoleId())) {
            throw new Exception("数据检查出现异常：角色不能为空");
        }
    }

    /**
     * 通用检查
     */
    @SneakyThrows
    private void checkUser(User user) {
        //检查用户名
        String username = user.getUsername();
        if (StringUtils.isEmpty(username)) {
            throw new Exception("数据检查出现异常：用户名为空");
        }
        if (!(username.length() > 1 && username.length() <= 20)) {
            throw new Exception("数据检查出现异常：用户名的长度应为1到20个字符");
        }
        //不做用户名唯一校验，允许用户名重复

        //检查姓名
        String name = user.getName();
        if (StringUtils.isEmpty(name)) {
            throw new Exception("数据检查出现异常：姓名为空");
        }
        if (!(name.length() > 1 && name.length() <= 20)) {
            throw new Exception("数据检查出现异常：姓名的长度应为1到20个字符");
        }
        //不做姓名唯一校验，允许姓名重复

        //校验手机号
        checkMobile(user.getMobile());
        //校验邮箱
        checkEmail(user.getEmail());
    }

    /**
     * 手机号码正则 验证手机号码格式
     * true 格式正确
     */
    @SneakyThrows
    private void checkMobile(String mobile) {
        if (mobile.length() != 11 || StringUtils.isEmpty(mobile)) {
            throw new Exception("数据检查出现异常：手机号码长度需要为11位");
        }
        if (!Pattern.compile("^((13[0-9])|(14[579])|(15([0-3]|[5-9]))|(16[56])|(17[0-8])|(18[0-9])|(19[1589]))\\d{8}$", Pattern.CASE_INSENSITIVE).matcher(mobile).matches()) {
            throw new Exception("数据检查出现异常：手机号码格式不正确");
        }
    }

    /**
     * 邮箱正则 验证邮箱格式
     * true 格式正确
     */
    @SneakyThrows
    private void checkEmail(String email) {
        if (StringUtils.isEmpty(email)) {
            throw new Exception("数据检查出现异常：邮箱不能为空");
        }
        if (!Pattern.compile("^[A-Za-z0-9\\u4e00-\\u9fa5]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$", Pattern.CASE_INSENSITIVE).matcher(email).matches()) {
            throw new Exception("数据检查出现异常：邮箱格式不正确");
        }
    }
}
